﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

using Pixysoft.Framework.XSchEmA.Entity;
using Pixysoft.Framework.XSchEmA;
using Pixysoft.Framework.XSchEmA.Sql;

namespace Pixysoft.Framework.XSchEmA.Aspect
{
    class OracleSchemaAspect : ISchemaAspect
    {
        List<string> dataTypeList;
        List<string> systemNameList;

        public OracleSchemaAspect()
        {
            dataTypeList = Pixysoft.EnumCoder.ToList<OracleDataTypeCollection>();
            systemNameList = Pixysoft.EnumCoder.ToList<OracleKeyWordCollection>();
        }



        DataTypeSchema ISchemaAspect.GetDefaultDataType()
        {
            return new DataTypeSchema(OracleDataTypeCollection.VARCHAR2.ToString(),
                50, -1, -1);
        }

        DataTypeSchema ISchemaAspect.AutoCompleteDataType(string datatype)
        {
            if (datatype == null)
                throw new Exception("010001");

            if (!dataTypeList.Contains(datatype))
                return new DataTypeSchema(OracleDataTypeCollection.VARCHAR2.ToString(),
                    100, -1, -1);

            OracleDataTypeCollection datatypeEnum = (OracleDataTypeCollection)Enum.Parse(typeof(OracleDataTypeCollection), datatype);

            switch (datatypeEnum)
            {
                case OracleDataTypeCollection.RAW:
                case OracleDataTypeCollection.CHAR:
                case OracleDataTypeCollection.NCHAR:
                case OracleDataTypeCollection.VARCHAR2:
                case OracleDataTypeCollection.NVARCHAR2:
                    return new DataTypeSchema(datatypeEnum.ToString(), 100, -1, -1);

                case OracleDataTypeCollection.FLOAT:
                    return new DataTypeSchema(datatypeEnum.ToString(), -1, 126, -1);

                case OracleDataTypeCollection.UROWID:
                    return new DataTypeSchema(datatypeEnum.ToString(), -1, 100, -1);

                case OracleDataTypeCollection.NUMBER:
                    return new DataTypeSchema(datatypeEnum.ToString(), -1, 38, 0);

                default:
                    return new DataTypeSchema(datatypeEnum.ToString(), -1, -1, -1);
            }
        }

        bool ISchemaAspect.IsValidDataType(DataTypeSchema datatype)
        {
            datatype.Inspection();

            if (!dataTypeList.Contains(datatype.DataType.ToUpper()))
                return false;

            OracleDataTypeCollection datatypeEnum = (OracleDataTypeCollection)Enum.Parse(typeof(OracleDataTypeCollection), datatype.DataType);

            switch (datatypeEnum)
            {
                case OracleDataTypeCollection.CHAR:
                case OracleDataTypeCollection.NCHAR:
                    if (datatype.Length < 1 || datatype.Length > 2000)
                        return false;
                    break;

                case OracleDataTypeCollection.VARCHAR2:
                case OracleDataTypeCollection.NVARCHAR2:
                    if (datatype.Length < 1 || datatype.Length > 4000)
                        return false;
                    break;

                case OracleDataTypeCollection.FLOAT:
                    if (datatype.Precision < 1 || datatype.Precision > 126)
                        return false;
                    break;

                case OracleDataTypeCollection.UROWID:
                    if (datatype.Precision < 1 || datatype.Precision > 4000)
                        return false;
                    break;

                case OracleDataTypeCollection.NUMBER:
                    if (datatype.Precision < 1 || datatype.Precision > 38)
                        return false;
                    if (datatype.Scale < 0 || datatype.Scale > datatype.Precision)
                        return false;
                    break;

                default:
                    break;
            }

            return true;
        }

        bool ISchemaAspect.IsUniqueDataType(DataTypeSchema datatype)
        {
            datatype.Inspection();

            if (datatype.DataType == "LONG")
                return true;

            if (datatype.DataType == "LONG_RAW")
                return true;

            return false;
        }

        List<string> ISchemaAspect.GetDataTypeList()
        {
            return Pixysoft.EnumCoder.ToList<OracleDataTypeCollection>();
        }

        List<string> ISchemaAspect.GetDataTypeRegexList()
        {
            List<string> list = new List<string>();
            list.Add(@"[\w]+\([\d]+\)[,]{0,1}");
            list.Add(@"[\w]+\([\d]+[,\d]*\)[,]{0,1}");

            list.Add(@"BINARY_DOUBLE[,]{0,1}");
            list.Add(@"BINARY_FLOAT[,]{0,1}");
            list.Add(@"URITYPE[,]{0,1}");

            list.Add(@"BLOB[,]{0,1}");
            list.Add(@"BFILE[,]{0,1}");

            list.Add(@"LONG[,]{0,1}");
            list.Add(@"ROWID[,]{0,1}");
            list.Add(@"CLOB[,]{0,1}");


            list.Add(@"NCLOB[,]{0,1}");
            list.Add(@"INTEGER[,]{0,1}");
            list.Add(@"DATE[,]{0,1}");
            return list;
        }

        public List<string> GetKeyWordsList()
        {
            return systemNameList;
        }

        string ISchemaAspect.GetDotNetDataType(DataTypeSchema datatypeSchema)
        {
            datatypeSchema.Inspection();

            OracleDataTypeCollection type = ((OracleDataTypeCollection)Enum.Parse(typeof(OracleDataTypeCollection), datatypeSchema.DataType));

            switch (type)
            {
                case OracleDataTypeCollection.BINARY_DOUBLE:
                case OracleDataTypeCollection.BINARY_FLOAT:
                case OracleDataTypeCollection.BFILE:
                case OracleDataTypeCollection.RAW:
                case OracleDataTypeCollection.BLOB:
                case OracleDataTypeCollection.CLOB:
                case OracleDataTypeCollection.NCLOB:
                case OracleDataTypeCollection.ROWID:
                case OracleDataTypeCollection.URITYPE:
                case OracleDataTypeCollection.UROWID:
                    return "System.Byte[]";

                case OracleDataTypeCollection.DATE:
                    return "System.DateTime";

                case OracleDataTypeCollection.FLOAT:
                case OracleDataTypeCollection.LONG:
                case OracleDataTypeCollection.NUMBER:
                    return "System.Double";

                case OracleDataTypeCollection.INTEGER:
                    return "System.Int64";

                case OracleDataTypeCollection.CHAR:
                case OracleDataTypeCollection.NCHAR:
                case OracleDataTypeCollection.NVARCHAR2:
                case OracleDataTypeCollection.VARCHAR2:
                default:
                    return "System.String";
            }
        }

        void ISchemaAspect.TransformToGeneral(DatabaseSchema schema)
        {
            foreach (TableSchema table in schema.Tables)
            {
                foreach (ColumnSchema column in table.Columns)
                {
                    switch ((OracleDataTypeCollection)Enum.Parse(typeof(OracleDataTypeCollection), column.DataType.DataType))
                    {
                        case OracleDataTypeCollection.LONG:
                        case OracleDataTypeCollection.FLOAT:
                            column.DataType = new DataTypeSchema(DotNetDataType.Double.ToString(),
                                -1, -1, -1, column.DataType.IsAutoIncrement);
                            break;
                        case OracleDataTypeCollection.ROWID:
                        case OracleDataTypeCollection.INTEGER:
                        case OracleDataTypeCollection.UROWID:
                            column.DataType = new DataTypeSchema(DotNetDataType.Int64.ToString(),
                                -1, -1, -1, column.DataType.IsAutoIncrement);
                            break;
                        case OracleDataTypeCollection.CLOB:
                        case OracleDataTypeCollection.NCLOB:
                        case OracleDataTypeCollection.URITYPE:
                        case OracleDataTypeCollection.RAW:
                        case OracleDataTypeCollection.BLOB:
                        case OracleDataTypeCollection.BFILE:
                            column.DataType = new DataTypeSchema(DotNetDataType.Byte.ToString(),
                                -1, -1, -1, column.DataType.IsAutoIncrement);
                            break;
                        case OracleDataTypeCollection.DATE:
                            column.DataType = new DataTypeSchema(DotNetDataType.DateTime.ToString(),
                                -1, -1, -1, column.DataType.IsAutoIncrement);
                            break;
                        case OracleDataTypeCollection.NUMBER:
                            column.DataType = new DataTypeSchema(DotNetDataType.Double.ToString(),
                                -1, column.DataType.Precision, column.DataType.Scale, column.DataType.IsAutoIncrement);
                            break;
                        default:
                        case OracleDataTypeCollection.NVARCHAR2:
                        case OracleDataTypeCollection.NCHAR:
                        case OracleDataTypeCollection.BINARY_DOUBLE:
                        case OracleDataTypeCollection.BINARY_FLOAT:
                        case OracleDataTypeCollection.CHAR:
                        case OracleDataTypeCollection.VARCHAR2:
                            column.DataType = new DataTypeSchema(DotNetDataType.String.ToString(),
                                column.DataType.Length, -1, -1, column.DataType.IsAutoIncrement);
                            break;
                    }
                }
            }
        }

        void ISchemaAspect.Transformation(DatabaseSchema database)
        {
            database.Info = new DatabaseInfo();
            database.Info.DatabaseType = DatabaseType.Oracle;

            foreach (TableSchema table in database.Tables)
            {
                foreach (ColumnSchema column in table.Columns)
                {
                    DotNetDataType type = Pixysoft.EnumCoder.ToEnum<DotNetDataType>(column.DataType.DataType);

                    switch (type)
                    {
                        case DotNetDataType.Boolean://2010-08-08 对应为数字
                        case DotNetDataType.Char://2010-08-08 对应为数字
                        case DotNetDataType.Int32:
                        case DotNetDataType.Int64:
                            column.DataType = new DataTypeSchema(OracleDataTypeCollection.LONG.ToString(),
                                -1, -1, -1, column.DataType.IsAutoIncrement);
                            break;

                        case DotNetDataType.Decimal:
                        case DotNetDataType.Single:
                        case DotNetDataType.Double:
                            if (column.DataType.Precision > 0)
                                column.DataType = new DataTypeSchema(OracleDataTypeCollection.NUMBER.ToString(),
                                    -1,
                                    column.DataType.Precision > 38 ? 38 : column.DataType.Precision,
                                    column.DataType.Scale > 38 ? 38 : column.DataType.Scale, column.DataType.IsAutoIncrement);
                            else
                                column.DataType = new DataTypeSchema(OracleDataTypeCollection.NUMBER.ToString(),
                                    -1, 38, 0, column.DataType.IsAutoIncrement);
                            break;

                        case DotNetDataType.DateTime:
                            column.DataType = new DataTypeSchema(OracleDataTypeCollection.DATE.ToString(),
                                -1, -1, -1, column.DataType.IsAutoIncrement);
                            break;


                        case DotNetDataType.Byte:
                            column.DataType = new DataTypeSchema(OracleDataTypeCollection.BLOB.ToString(),
                                -1, -1, -1, column.DataType.IsAutoIncrement);
                            break;

                        case DotNetDataType.String:
                        default:
                            if (column.DataType.Length > 0)
                                column.DataType = new DataTypeSchema(OracleDataTypeCollection.VARCHAR2.ToString(),
                                 column.DataType.Length > 4000 ? 4000 : column.DataType.Length, -1, -1, column.DataType.IsAutoIncrement);
                            else
                                column.DataType = new DataTypeSchema(OracleDataTypeCollection.VARCHAR2.ToString(),
                                    255, -1, -1, column.DataType.IsAutoIncrement);
                            break;
                    }
                }
            }
        }





        bool ISchemaAspect.IsValidName(string name)
        {
            if (name == null)
                return false;

            if (name.Trim() == "")
                return false;

            if (systemNameList.Contains(name.ToUpper()))
                return false;

            if (name.Length > 30)
                return false;

            if (Regex.Match(name, "[a-z,A-Z]{1}[a-z,A-Z,1-9,_,$,#]*").Value != name)
                return false;

            return true;
        }

        string ISchemaAspect.ValidateDefaultValue(string input, DataTypeSchema datatype)
        {
            if (input == null)
                return null;

            OracleDataTypeCollection type = ((OracleDataTypeCollection)Enum.Parse(typeof(OracleDataTypeCollection), datatype.DataType));

            switch (type)
            {
                case OracleDataTypeCollection.BINARY_DOUBLE:
                case OracleDataTypeCollection.BINARY_FLOAT:
                case OracleDataTypeCollection.BFILE:
                case OracleDataTypeCollection.RAW:
                case OracleDataTypeCollection.BLOB:
                case OracleDataTypeCollection.CLOB:
                case OracleDataTypeCollection.NCLOB:
                case OracleDataTypeCollection.ROWID:
                case OracleDataTypeCollection.URITYPE:
                case OracleDataTypeCollection.UROWID:
                case OracleDataTypeCollection.DATE:
                    return null;


                case OracleDataTypeCollection.FLOAT:
                case OracleDataTypeCollection.LONG:
                case OracleDataTypeCollection.NUMBER:
                    {
                        try { return Double.Parse(input).ToString(); }
                        catch
                        {
                            return null;
                        }
                    }

                case OracleDataTypeCollection.INTEGER:
                    try
                    {
                        return Int64.Parse(input).ToString();
                    }
                    catch
                    {
                        return null;
                    }

                case OracleDataTypeCollection.CHAR:
                case OracleDataTypeCollection.NCHAR:
                case OracleDataTypeCollection.NVARCHAR2:
                case OracleDataTypeCollection.VARCHAR2:
                default:
                    try
                    {
                        return Double.Parse(input).ToString();
                    }
                    catch
                    {
                        return "'" + input.Replace("'", "") + "'";
                    }
            }
        }

        public ISqlBuilder SqlBuilder
        {
            get
            {
                return OracleSqlBuilder.Instance;
            }
        }




        public void CreateDatabase(ISchemaCommand command, string databasename)
        {
            command.SQL = SqlBuilder.CreateDatabase(databasename);

            command.Execute();
        }

        public void DropDatabase(ISchemaCommand command, string databasename)
        {
            command.SQL = SqlBuilder.DropDatabase(databasename);

            command.Execute();
        }
    }
}
